#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Datetime: 2019/5/13 14:36
# @Author  : CHEN Wang
# @Site    :
# @File    : celebrity.py
# @Software: PyCharm

"""
脚本说明：常用的数据类型转换，等
"""

import pandas as pd
import numpy as np
import itertools
from functools import reduce
from quant_researcher.quant.project_tool.logger.my_logger import LOG


def str_a_dict(a_dict):
    """
    把一个 dict 对象转成字符串，特殊处理可能遇到的特殊对象，方便打印输出
        如果某一个值是 pd.DataFrame，显示成：⛽ DF 🚚
    :param a_dict: 字典对象
    :return: str，经过转换的 string
    """
    tmp = {}
    for k, v in a_dict.items():
        if isinstance(v, pd.DataFrame):
            tmp.update({k: '⛽ DF 🚚'})
        else:
            tmp.update({k: v})
    return str(tmp)


def print_obj_size(x, name):
    """
    打印对象所占内存大小（暂时支持单位：MB）

    :param any_obj x: 被检测的对象
    :param str name: 对象的名字，方便寻找&区分
    :return:
    """
    LOG.info(f'{name} 占用内存：%.2f(MB)' % (x.__sizeof__() / 1024 / 1024))


def get_lst_condition(a_lst, key):
    """
    把一个 list 转成用于 SQL 查询条件
        如：key=u_code，a_lst=['111', '999']，输出：u_code in ('111', '999')
    :param a_lst: 可选值
    :param str key: 键
    :return: string
    """
    if len(a_lst) == 0:
        raise ValueError('不应该传入空数组')
    elif len(a_lst) == 1:
        return f'{key}=\'{a_lst[0]}\''
    else:
        ans = map(lambda x: f'\'{x}\'', a_lst)
        ans = ', '.join(ans)
        ans = f'{key} in ({ans})'
        return ans


def try_this_func(desc, func, args, kwargs, swallow_err=False):
    """
    根据要求，尝试运行某个函数

    :param str desc: 这个函数的描述，方便从日志中区分&寻找
    :param function func: 哪个函数
    :param tuple args: 传给函数的位置参数
    :param dict kwargs: 传给函数的关键字参数
    :param bool swallow_err: 是否忽略函数运行时发生的错误，默认：None
    :return: 返回对应函数的返回
    """
    try:
        return func(*args, **kwargs)
    except Exception as err:
        err_msg = f'尝试 {desc} 的时，👻：{str(err)}'
        LOG.error(err_msg)
        if not swallow_err:
            raise err
        else:
            LOG.error(f'我把这个错误({err_msg})吞下去了↑💪↓')


def update_a_dict(the_dict, key, value):
    """
    更新一个字典中某个键的值
        根据 key 寻找字典中是否含有该键，如有则将 value 与之前存在的值相加；否则直接 update
        适用于一些需要循环将字典中某个键的值增加或减少
    :param the_dict: 那个字典
    :param key: 键
    :param value: 值
    :return:
    """
    tmp = value
    if the_dict.__contains__(key):
        tmp += the_dict[key]
    the_dict.update({key: tmp})


def to_percent_str(value, **kwargs):
    """
    把一个 int 或 float 转成百分比 str 的形式输出
        - 如：0.1133，输出：11.33%
        - 可指定：保留小数位数
        - 可指定：是否在末尾加上百分号 %
    :param value: 将要执行转换的值
    :param kwargs:
        - digit，int，需要的小数位数，默认：2
        - with_sign，bool，是否需要在后面加上百分号 %，默认：True
    :return: string
    """
    digit = kwargs.pop('digit', 2)
    with_sign = kwargs.pop('with_sign', True)
    c = '%.' + str(digit) + 'f'
    d = c % (value * 100)
    if with_sign:
        d += '%'
    return d


def round_2_digit(data_list, digit_num=4, rescale=True):
    """
    把list数据保留到对应digit_num位数, 注意权重之和只能偏离1一点点，否则可能出现无法将权重和调整为1

    :param list data_list: 如[0.5042533559036255, 0.4952854236, 0.000005]
    :param int digit_num: 保留小数位， 默认为4
    :param bool rescale: 是否将数据调整为和为1， 默认为True
    :return: list，满足要求的 list
    :raise ValueError: 如果不能完成转换任务
    :return:
    """

    data_len = [len(str(i)) for i in data_list]            # 位数长度
    max_len_data_idx = data_len.index(max(data_len))       # 第一个位数最长的数据对应的索引

    data_list = [round(i, digit_num) for i in data_list]

    while (abs(sum(data_list)-1) >= 1e-10) and rescale:  # 允许极小的误差1e-10
        delta = 1 - sum(data_list)
        data_list[max_len_data_idx] += delta  # 将超出的数据加到位数最多的数据上
        data_list = [round(i, digit_num) for i in data_list]

    return data_list


def to_fixed_digit(value, **kwargs):
    """
    将一个值，如果是float，则转成固定位数；否则，原封不动返回

    :param value: 那个值，最好是float
    :param kwargs:
        - digit，int，小数位数
        - log_err_if_not_float，bool，如非 float，是否 log 一个 error，默认：False
    :return: 经过转换的值
    """
    if isinstance(value, float):
        digit = kwargs.pop('digit', 3)
        c = '%.' + str(digit) + 'f'
        d = c % value
        return float(d)
    else:
        log_err_if_not_float = kwargs.pop('log_err_if_not_float', False)
        if log_err_if_not_float:
            LOG.error(f'遇到一个非float数据：{value}')
        return value


# 🛠TODO: stock_list中有股票的中文/stock_block中有版块的中文 需要将他们做一些模糊查询
def split_word(input_text, cutall=False):
    """
    使用jieba分词 将输入的语句分词


    这里主要是一些关于文本的代码
    文本分词
    模糊查询
    正则匹配
    """
    import jieba
    import re
    import fuzzyfinder

    return jieba.cut(input_text,cut_all=cutall)


def QA_util_multi_demension_list(row_, col_=0):
    # row_ 是行, col_ 是列
    """
    如果需要创建一个[[],[]], 那就用 row_=2,col=0
    其他时候,返回的都是[[None]]
    """
    return [[None for col in range(col_)] for row in range(row_)]


def QA_util_diff_list(datastruct):
    return (np.array(datastruct[1:]) - np.array(datastruct[:-1])).tolist()


def QA_util_dict_remove_key(dicts, key):
    """
    输入一个dict 返回删除后的
    """

    if isinstance(key, list):
        for item in key:
            try:
                dicts.pop(item)
            except:
                pass
    else:
        try:
            dicts.pop(key)
        except:
            pass
    return dicts


def union_filter(data_list, how_to_deal='and'):
    """
    将不同条件筛选得到的data_list根据and或者or进行合并

    :param list data_list: list中的每个元素都为一个list
    :param str how_to_deal: 求交集还是并集，and-求交集，or-求并集
    :return: list
    """
    if len(data_list) <= 1:
        return data_list
    else:
        if how_to_deal == 'and':
            return list(reduce(lambda x, y: set(x) & set(y), data_list))
        elif how_to_deal == 'or':
            return list(reduce(lambda x, y: set(x) | set(y), data_list))
        else:
            raise NotImplementedError


def combination(data_list, num):
    """

    :param data_list:
    :param num:
    :return:
    """
    pass
    # if num == 2:
    #     list(itertools.combinations(n100_p5000_threshold_2_2, 2))


if __name__ == '__main__':
    a = round_2_digit([0.5042533559036255, 0.4952854236, 0.00000005], 4)
    a = to_fixed_digit(0.35)